package com.yunbiao.ybbusstopplate.play_sync;

import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.NetworkUtils;
import com.blankj.utilcode.util.ZipUtils;
import com.xuhao.didi.core.iocore.interfaces.IPulseSendable;
import com.xuhao.didi.core.iocore.interfaces.ISendable;
import com.xuhao.didi.core.pojo.OriginalData;
import com.xuhao.didi.socket.client.impl.client.action.ActionDispatcher;
import com.xuhao.didi.socket.client.sdk.OkSocket;
import com.xuhao.didi.socket.client.sdk.client.ConnectionInfo;
import com.xuhao.didi.socket.client.sdk.client.OkSocketOptions;
import com.xuhao.didi.socket.client.sdk.client.connection.DefaultReconnectManager;
import com.xuhao.didi.socket.client.sdk.client.connection.IConnectionManager;
import com.xuhao.didi.socket.common.interfaces.default_protocol.DefaultNormalReaderProtocol;
import com.yunbiao.ybbusstopplate.utils.NetController;
import com.yunbiao.ybbusstopplate.utils.SpTool;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

/**
 * 副屏幕连接主屏幕
 */
public class ClientSocket {

    private static final String TAG = "ClientSocket";

    private Disposable socketDisposable;

    //  private Disposable fileDisposable;

    private IConnectionManager iConnectionManager;

    private Handler handler;

    private final StringBuilder stringBuilder = new StringBuilder();

    private Runnable runnable;

    private SocketCallback socketCallback;

    private  BufferedOutputStream bufferedOutputStream = null;

    private  String fileName = "";

    private static final class Holder {
        public static final ClientSocket INSTANCE = new ClientSocket();
    }

    public static ClientSocket getInstance() {
        return Holder.INSTANCE;
    }

    private ClientSocket() {

    }

    public void setSocketCallback(SocketCallback socketCallback) {
        Log.d(TAG, " 设置SocketCallback ");
        this.socketCallback = socketCallback;
    }

    public String getMessage() {
        return stringBuilder.toString();
    }

    public void setRunnable(Runnable runnable) {
        this.runnable = runnable;
    }

    public void runUpdate() {
        if (runnable != null) {
            runnable.run();
        }
    }

    public void inputLog(String log) {
        stringBuilder.append("\n").append(log);
        runUpdate();
    }

    public void sendMessage(String string) {
        sendMessage(new SocketDataBean(string));
    }

    public void sendMessage(SocketDataBean msgDataBean) {
        if (iConnectionManager.isConnect()) {
            iConnectionManager.send(msgDataBean);
        }
    }

    public static class NoneIpException extends Exception {
    }
    public static class PingFailedException extends Exception {
    }

    private final AtomicInteger pingRetry = new AtomicInteger(0);

    public void startAutoConnect() {
        handler = new Handler(Looper.getMainLooper());
        //    startFileDownloader();
        socketDisposable = Observable
                .fromCallable(() -> {
                    String ethIpAddress = NetController.getEthIpAddress();
                    stringBuilder.append("\n").append("本机IP：").append(ethIpAddress);
                    runUpdate();

                    String[] split = ethIpAddress.split("\\.");
                    //String targetIp = split[0] + "." + split[1] + "." + split[2] + "." + "100";
                    String targetIp = SpTool.Companion.getMainScreenAddress();
                    Log.d(TAG, "本机IP：" + ethIpAddress + " -----> 目标IP: " + targetIp);
                    stringBuilder.append("\n").append("目标IP：").append(targetIp);
                    runUpdate();

                    if (targetIp.startsWith("0.0.0.")) {
                        Log.d(TAG, "startAutoConnect: DHCP不成功");
                        stringBuilder.append("\n").append("DHCP不成功");
                        runUpdate();
                       // throw new WaitPlayView.NoneIpException();
                        throw new NoneIpException();
                    }
                    if (!NetworkUtils.isAvailableByPing(targetIp)) {
                        stringBuilder.append("\n").append("无法ping通目标地址：" + targetIp);
                        runUpdate();
                        // throw new WaitPlayView.PingFailedException();
                        throw new PingFailedException();
                    }

//                    boolean isAvailable = NetworkUtils.isAvailable();
//                    Log.d(TAG, "网络状态：" + isAvailable);
//                    stringBuilder.append("网络状态：").append(isAvailable).append("\n");
//                    runUpdate();

                    return targetIp;
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .retryWhen(throwableObservable ->
                        throwableObservable.flatMap((Function<Throwable, ObservableSource<?>>) throwable -> {
                           // if (throwable instanceof WaitPlayView.NoneIpException) {
                            if (throwable instanceof NoneIpException) {
                                Log.d(TAG, "startAutoConnect: DHCP不成功：切换DHCP，10秒后重试");
                                stringBuilder.append("\n").append("DHCP不成功：切换DHCP，10秒后重试");
                                runUpdate();

                                NetController.openSubScreenDhcp();
                                return Observable.timer(10, TimeUnit.SECONDS);
                            }
                            if (pingRetry.get() >= 5) {
                                pingRetry.set(0);
                                Log.d(TAG, "Ping失败超过5次，切换DHCP，10秒后重试");
                                stringBuilder.append("\n").append("Ping失败超过5次，切换DHCP，10秒后重试");
                                runUpdate();

                                NetController.openSubScreenDhcp();
                                return Observable.timer(10, TimeUnit.SECONDS);
                            }
                            pingRetry.incrementAndGet();

                            stringBuilder.append("\n").append("2秒后重试");
                            runUpdate();

                            return Observable.timer(2, TimeUnit.SECONDS);
                        })
                )
                .repeatWhen(objectObservable ->
                        objectObservable.flatMap((Function<Object, ObservableSource<?>>) o ->
                                Observable.timer(5, TimeUnit.SECONDS)))
                .subscribe(ip -> {
                    if (iConnectionManager == null) {
                        Log.d(TAG, "startAutoConnect: 开始连接：" + ip);
                        stringBuilder.append("\n").append("开始连接：").append(ip);
                        runUpdate();
                        openSocket(ip);
                    } else {

                        ConnectionInfo connectionInfo = iConnectionManager.getRemoteConnectionInfo();
                        if (!TextUtils.equals(ip, connectionInfo.getIp())) {
                            Log.d(TAG, "startAutoConnect: IP发生变化，开始重连：" + ip);
                            stringBuilder.append("\n").append("IP发生变化，切换地址：").append(ip);
                            runUpdate();
                            closeSocket();
                            SystemClock.sleep(20);
                            openSocket(ip);
                            //  iConnectionManager.switchConnectionInfo(new ConnectionInfo(ip, ServerSocket.SERVER_PORT));
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "accept: ", throwable);
                    }
                });
    }

    public void release() {
        stopSocket();
        closeSocket();
    }

    private void openSocket(String ip) {
        iConnectionManager = OkSocket
                .open(ip, ServerSocket.SERVER_PORT)
                .option(new OkSocketOptions
                        .Builder()
                        .setReaderProtocol(new DefaultNormalReaderProtocol())
                        .setIOThreadMode(OkSocketOptions.IOThreadMode.DUPLEX)
                        .setCallbackThreadModeToken(new OkSocketOptions.ThreadModeToken() {
                            @Override
                            public void handleCallbackEvent(ActionDispatcher.ActionRunnable actionRunnable) {
                                handler.post(actionRunnable);
                            }
                        })
                        .setReadByteOrder(ByteOrder.BIG_ENDIAN)
                        .setMaxReadDataMB(6)
                        .build()
                )
                .registerReceiver(defaultReconnectManager);
        iConnectionManager.connect();
    }

    private void closeSocket() {
        if (iConnectionManager != null) {
            if (iConnectionManager.isConnect()) {
                iConnectionManager.disconnect();
            }
            iConnectionManager = null;
        }
    }

    private final DefaultReconnectManager defaultReconnectManager = new DefaultReconnectManager() {
        @Override
        public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
            super.onSocketConnectionSuccess(info, action);
            Log.d(TAG, "onSocketConnectionSuccess: " + action);

            try {
                stringBuilder.append("\n")
                        .append("已连接：").append(info.getIp())
                        .append(",action:").append(action);
                runUpdate();
            } catch (Exception e) {
                stringBuilder.append("\n")
                        .append("onSocketConnectionSuccess: ").append(e.getMessage());
                runUpdate();
            }
            iConnectionManager
                    .getPulseManager()
                    .setPulseSendable((IPulseSendable) "心跳"::getBytes)
                    .pulse();
        }

        @Override
        public void onSocketConnectionFailed(ConnectionInfo info, String action, Exception e) {
            super.onSocketConnectionFailed(info, action, e);
            Log.d(TAG, "onSocketConnectionFailed: ", e);
            try {
                stringBuilder.append("\n")
                        .append("连接失败：").append(info.getIp())
                        .append(",action:").append(action)
                        .append(",exception:").append(e.getMessage());
                runUpdate();
            } catch (Exception e1) {
                stringBuilder.append("\n")
                        .append("onSocketConnectionFailed: ").append(e1.getMessage());
                runUpdate();
            }
        }

        @Override
        public void onSocketDisconnection(ConnectionInfo info, String action, Exception e) {
            super.onSocketDisconnection(info, action, e);
            Log.d(TAG, "onSocketDisconnection: ", e);
            try {
                stringBuilder.append("\n")
                        .append("断开连接：").append(info.getIp())
                        .append(",action:").append(action)
                        .append(",exception:").append(e.getMessage());
                runUpdate();
            } catch (Exception e1) {
                stringBuilder.append("\n")
                        .append("onSocketDisconnection: ").append(e.getMessage());
                runUpdate();
            }
        }

        @Override
        public void onPulseSend(ConnectionInfo info, IPulseSendable data) {
            super.onPulseSend(info, data);
            Log.d(TAG, "onPulseSend: " + new String(data.parse()));
            iConnectionManager.getPulseManager().feed();
        }

        @Override
        public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
            super.onSocketReadResponse(info, action, data);
            try {
//                stringBuilder.append("\n")
//                        .append("收到消息<--").append(info.getIp())
//                        .append(",action:").append(action)
//                        .append(",data:").append(new String(data.getBodyBytes()));
                runUpdate();
            } catch (Exception e) {
                stringBuilder.append("\n")
                        .append("onSocketReadResponse: ").append(e.getMessage());
                runUpdate();
            }

            try {
                String str = new String(data.getBodyBytes(), StandardCharsets.UTF_8);
                Log.d(TAG, "onSocketReadResponse: " + "接收数据的长度：" + str.length() + "," + str);
                String actionFlag = SocketUtil.getAction(str);
                switch (actionFlag) {
                    case "1":
                        PlayVideoData playVideoData = GsonUtils.fromJson(new String(data.getBodyBytes()),  PlayVideoData.class);
                        if (socketCallback != null) {
                            socketCallback.receiveMessage(Integer.parseInt(actionFlag),  playVideoData);
                        }
                        break;
                    case "0":
//                        ProgramFile programFile = GsonUtils.fromJson(new String(data.getBodyBytes()), ProgramFile.class);
//                        if(!fileName.equals(programFile.getFileName())&&bufferedOutputStream!=null){
//                            bufferedOutputStream.close();
//                            bufferedOutputStream = null;
//                        }
//                        File destfile = new File(programFile.getDestPath());
//                        // Material material = DaoManager.get().queryMaterialByProgramIdAndMaterialId(programFile.programId, programFile.materialId);
//                        if(destfile.exists()){
//                            return;
//                        }
//                        File tempFile = new File(Path.getTempDir(),programFile.getFileName());
//                       // Log.d(TAG, programFile.flag+"接收文件数据: " + programFile.materialPath+","+tempFile.getPath());
//                        byte[] buffer = BytesUtils.hexToByteArray(programFile.getData());
//
//                        if(programFile.getFlag() ==0 &&tempFile.exists()){
//                            tempFile.delete();
//                            Log.d(TAG, "开始接收文件数据: " +programFile.getFlag());
//                            return;
//                        }else if (programFile.getFlag() == -1&&bufferedOutputStream!=null) {
//                            bufferedOutputStream.close();
//                            Log.d(TAG, "结束接收文件数据: " +programFile.getFlag());
//                            if (!FileUtils.move(tempFile,destfile)) {
//                                throw new Exception("destCacheFile move to destFile failed,unknown error");
//                            }
//                            return;
//                        }else if(programFile.getFlag() ==1){
//                            Log.d(TAG, "正在接收文件数据: " +programFile.getFlag());
//                        }
////                        long currentSize = programFile.size;
////                        int percent = 0;
//                        //判断是否可以断点下载
//                        long serverFileSize = programFile.getMaterial().getSize();
//
//                        Log.d(TAG, "impDownload: 服务器文件大小：" + serverFileSize+"数据包"+buffer.length);
//                        long localFileSize = 0;
//                        if (tempFile.exists()) {
//                            localFileSize = tempFile.length();
//                            if (localFileSize >= serverFileSize) {
//                                tempFile.delete();
//                                localFileSize = 0;
//                            }
//                        }
//                      Log.d(TAG, "impDownload: 本地文件大小：" + localFileSize);
//
////                        //设置断点下载
////                        if (localFileSize > 0) {
////                            ftpClient.setRestartOffset(localFileSize);
////                        }
////                        long currentSize = localFileSize;
//                        try {
//                            if(bufferedOutputStream==null) {
//                                // 文件要打开追加模式
//                                bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(tempFile, true));
//                            }
//                          //  bos.write(buffer, 0, buffer.length);
//                            bufferedOutputStream.write(buffer); // 写入数据
//                            bufferedOutputStream.flush(); // 刷新缓冲区
//                            if (tempFile.length()>= programFile.getMaterial().getSize()) {
//                                bufferedOutputStream.close();
//                                bufferedOutputStream = null;
//                                if (!FileUtils.move(tempFile,destfile)) {
//                                    throw new Exception("destCacheFile move to destFile failed,unknown error");
//                                }
//                               Material material = DaoManager.get().queryMaterialByProgramIdAndMaterialId(
//                                       programFile.getMaterial().getProgramId(), programFile.getMaterial().getId());
//                                if (material == null) {
//                                    SocketUtil.setMaterial(programFile.getMaterial());
//                                }
//                            }
//
////                                int p = BigDecimal.valueOf(currentSize)
////                                        .divide(BigDecimal.valueOf(serverFileSize), 2, RoundingMode.HALF_UP)
////                                        .multiply(BigDecimal.valueOf(100)).intValue();
////                                if(p - percent >= 10){
////                                    percent = p;
////                                    Log.d(TAG, "impDownload: 下载进度：" + percent);
////                                    onProgress(percent);
////                                }
//                        } catch (Exception e) {
//                            Log.e(TAG, "impDownload: " + e.getMessage());
//                            throw new Exception("download failed: " + e.getMessage());
//                        }
                        break;
                }
                // UiMessageUtils.getInstance().send(UiEvent.NOTIFY_PLAY_MATERIAL, waitPlay);
            } catch (Exception e) {
                Log.e(TAG, "onSocketReadResponse: ", e);
            }

        }

        @Override
        public void onSocketWriteResponse(ConnectionInfo info, String action, ISendable data) {
            super.onSocketWriteResponse(info, action, data);
            Log.d(TAG, "onSocketWriteResponse: " + new String(data.parse()));
            try {
                stringBuilder.append("\n")
                        .append("发送消息-->").append(info.getIp())
                        .append(",action:").append(action)
                        .append(",data:").append(new String(data.parse()));
                runUpdate();
            } catch (Exception e) {
                stringBuilder.append("\n")
                        .append("onSocketWriteResponse: ").append(e.getMessage());
                runUpdate();
            }

        }

        @Override
        public void onSocketIOThreadStart(String action) {
            super.onSocketIOThreadStart(action);
            Log.d(TAG, "onSocketIOThreadStart: " + action);
            stringBuilder.append("\n")
                    .append("IO线程启动-->").append(action);
            runUpdate();
        }

        @Override
        public void onSocketIOThreadShutdown(String action, Exception e) {
            super.onSocketIOThreadShutdown(action, e);
            Log.d(TAG, "onSocketIOThreadShutdown: " + action, e);
            stringBuilder.append("\n")
                    .append("IO线程中断-->").append(action)
                    .append(",exception:").append(e.getMessage());
            runUpdate();
        }
    };

    private void stopSocket() {
        if (socketDisposable != null && !socketDisposable.isDisposed()) {
            socketDisposable.dispose();
            socketDisposable = null;
        }
    }


//    private String msgSerial;
//
//    private String resourceId;
//
//    private List<Material> materialList;
//
//    private String destFilePath;
//
//    protected String tempDir;
//
//    public void startFileDownloader() {
////        this.msgSerial = msgSerial;
////        this.resourceId = resourceId;
//        this.materialList = ProgramLoader.loadDownloadList();
//        tempDir = Path.getTempDir();
//        if (materialList.isEmpty()) {
//            d("start: 下载列表为空");
//            return;
//        }
//        if (fileDisposable != null && !fileDisposable.isDisposed()) {
//            d("start: 下载正在进行");
//            return;
//        }
//
//        fileDisposable = Observable.fromIterable(materialList)
//                .map(material -> {
//                    StorageChecker.checkAndDelete(material.getSize());
//                    return material;
//                })
//                .concatMap(material -> {
//                    d("开始下载：" + Path.getMaterialPath(material));
//                    return download(material).onErrorResumeNext(Observable.just(new File("")));
//                })
//                .subscribeOn(Schedulers.io())
//                .observeOn(AndroidSchedulers.mainThread())
//                .retryWhen(errors -> errors
//                        .zipWith(Observable.range(1, 3), (throwable, integer) -> new Object[]{integer, throwable})
//                        .flatMap(objects -> {
//                            int times = (int) objects[0];
//                            Throwable throwable = (Throwable) objects[1];
//                            if (times >= 3) {
//                                return Observable.error(throwable);
//                            } else {
//                                return Observable.timer(30, TimeUnit.SECONDS);
//                            }
//                        }))
////                .repeatWhen(objectObservable -> objectObservable.delay(3, TimeUnit.SECONDS))//三秒无限重试
//                .subscribe(
//                        file -> {
//                            if (TextUtils.isEmpty(file.getPath())) {
//                                d("下载失败: ");
//                            } else {
//                                d("下载完成: " + file.getPath());
//                            }
//                            if (SocketUtil.allComplete()) {
//                                clear();
//                            }
//                        },
//                        throwable -> {
//                            Log.e(TAG, "下载失败: ", throwable);
//                        }
//                );
//    }
//
//    public Observable<File> download(Material material) {
//        Observable<File> observable;
//        if (!TextUtils.isEmpty(destFilePath) && FileUtils.isFileExists(destFilePath)) {
//            observable = Observable.just(new File(destFilePath));
//        } else {
//           // observable = implDownload().map(handleFile());
//            observable = implDownload(material);
//        }
//
//        return observable
//                .retryWhen(errors -> errors
//                        .zipWith(Observable.range(1, 3), (throwable, integer) -> new Object[]{integer, throwable})
//                        .flatMap(objects -> {
//                            int times = (int) objects[0];
//                            Throwable throwable = (Throwable) objects[1];
//                            if (times >= 3) {
//                                return Observable.error(throwable);
//                            } else {
//                                return Observable.timer(30, TimeUnit.SECONDS);
//                            }
//                        }))
//                .doOnSubscribe(disposable -> {
//                        String fileRequest = SocketUtil.getFileData(material,new byte[1],0);
//                        sendMessage(fileRequest);
//                });
////                .doOnError(this::onError)
////                .doOnNext(this::onComplete);
//    }
//
//    private Function<File, File> handleFile() {
//        return tempFile -> {
//            File destTempFile = tempFile;
//            if (tempFile.getName().endsWith(".zip")) {
//                //解压到目标缓存路径
//                List<File> files = ZipUtils.unzipFile(tempFile, tempFile.getParentFile());
//                FileUtils.delete(tempFile);
//                if (files == null || files.isEmpty()) {
//                    throw new Exception("unzip failed,unknown error");
//                }
//                //取第一个文件
//                destTempFile = files.get(0);
//            }
//
//            if (!TextUtils.isEmpty(destFilePath)) {
//                String fileExtension = FileUtils.getFileExtension(destTempFile);
//                if (!TextUtils.isEmpty(fileExtension)) {
//                    fileExtension = "." + fileExtension;
//                }
//                //移动目标文件
//                File destFile = new File(destFilePath + fileExtension);
//                if (!FileUtils.move(destTempFile, destFile)) {
//                    throw new Exception("destCacheFile move to destFile failed,unknown error");
//                }
//                return destFile;
//            }
//            return destTempFile;
//        };
//    }
//
//    /**
//     * 下载过程
//     *
//     * @return
//     */
//    protected Observable<File> implDownload(Material material) {
//        return Observable
//                .fromCallable(() -> {
//                    if (!NetworkUtils.isAvailable()) {
//                        throw new Exception("network is unavailable");
//                    }
//                    return new Object();
//                })
//                .map(object -> {
//
//                    File tempFile = new File(destFilePath);
//                    if ( FileUtils.isFileExists(destFilePath) && tempFile.length()==material.getSize()) {
//                        return tempFile;
//                    } else {
//                        throw new Exception("ftpClient completePendingCommand is false");
//                    }
//                });
//    }
//
//
//    private void d(String log) {
//        Log.d(TAG, log);
//        if (ScreenPikerUtils.isMasterScreen()) {
//            ServerSocket.getInstance().inputLog(log);
//        } else {
//            ClientSocket.getInstance().inputLog(log);
//        }
//    }
//
//    public void clear() {
//        if (fileDisposable != null && !fileDisposable.isDisposed()) {
//            fileDisposable.dispose();
//            fileDisposable = null;
//        }
//    }

}
